#pragma once

#include <boost/unordered_map.hpp>
#include <nanobdd/common.h>
#include <nanobdd/node.h>
#include <forward_list>
#include <list>
#include <map>
#include <optional>
#include <tuple>
#include <vector>

namespace nanobdd {

class ListNodeCache {
 public:
  ListNodeCache() = default;
  ListNodeCache(size_t size) : size_(size) {
    cache_.resize(size);
  }
  
  struct CacheEntry {
    Node* node;
    uint32_t level;
    Node* low;
    Node* high;
  };

  void
  insert(uint32_t hash, Node* node) {
    auto& entry = cache_.at(hash);
    CacheEntry ce;
    ce.node = node;
    ce.level = node->level();
    ce.low = node->low();
    ce.high = node->high();
    entry[std::make_tuple(ce.level, ce.low, ce.high)] = std::move(ce);
    // entry.push_front(std::move(ce));
  }

  // std::optional<std::shared_ptr<Node>>
  Node*
  lookup(uint32_t hash,
      uint32_t level, Node* low, Node* high) {
    

    auto& entry = cache_.at(hash);
    for (auto& e : entry) {
      auto it = entry.find(std::make_tuple(level, low, high));
      if (it != entry.end()) {
        return it->second.node;
      }
      // if (e.level == level && e.low == low && e.high == high) {
      //   return e.node;
      // }
    }

    // return std::nullopt;
    return nullptr;
  }

  void
  resize(size_t size) {
    size_ = size;
    cache_.resize(size);
  }

  size_t
  size() {
    return cache_.size();
  }

  auto&
  cache() {
    return cache_;
  }

 private:
  uint32_t size_;
  std::vector<std::map<std::tuple<uint32_t, Node*, Node*>, CacheEntry>> cache_;
};

} // namespace nanobdd